Linux support for sysenter/exit on x86_32.
authorIan.Campbell@xensource.com <Ian.Campbell@xensource.com>
Fri, 21 Apr 2006 16:19:31 +0000 (17:19 +0100)
committerIan.Campbell@xensource.com <Ian.Campbell@xensource.com>
Fri, 21 Apr 2006 16:19:31 +0000 (17:19 +0100)
This support is only active when supervisor_mode_kernel is enabled
in the Xen build (and when the hardware supports sysenter).

Signed-off-by: Ian Campbell <ian.campbell@xensource.com>
linux-2.6-xen-sparse/arch/i386/kernel/entry-xen.S
linux-2.6-xen-sparse/arch/i386/kernel/sysenter.c

index 78ad19ef2b957a80cd5c4547b2f561fd5af17a2f..770d2e1fb44ae24e332b88e074f3e4f1dc6c3402 100644 (file)
@@ -239,7 +239,7 @@ sysenter_past_esp:
        jae syscall_badsys
        call *sys_call_table(,%eax,4)
        movl %eax,EAX(%esp)
-       cli
+       DISABLE_INTERRUPTS
        movl TI_flags(%ebp), %ecx
        testw $_TIF_ALLWORK_MASK, %cx
        jne syscall_exit_work
@@ -247,8 +247,23 @@ sysenter_past_esp:
        movl EIP(%esp), %edx
        movl OLDESP(%esp), %ecx
        xorl %ebp,%ebp
+#ifdef CONFIG_XEN
+       __ENABLE_INTERRUPTS
+sysexit_scrit: /**** START OF SYSEXIT CRITICAL REGION ****/
+       __TEST_PENDING
+       jnz  14f                        # process more events if necessary...
+       movl ESI(%esp), %esi
+       sysexit
+14:    __DISABLE_INTERRUPTS
+sysexit_ecrit: /**** END OF SYSEXIT CRITICAL REGION ****/
+       push %esp
+       call evtchn_do_upcall
+       add  $4,%esp
+       jmp  ret_from_intr
+#else
        sti
        sysexit
+#endif /* !CONFIG_XEN */
 
 
        # system call handler stub
@@ -530,6 +545,11 @@ error_code:
 # So, on entry to the handler we detect whether we interrupted an
 # existing activation in its critical region -- if so, we pop the current
 # activation and restart the handler using the previous one.
+#
+# The sysexit critical region is slightly different. sysexit
+# atomically removes the entire stack frame. If we interrupt in the
+# critical region we know that the entire frame is present and correct
+# so we can simply throw away the new one.
 ENTRY(hypervisor_callback)
        pushl %eax
        SAVE_ALL
@@ -538,6 +558,11 @@ ENTRY(hypervisor_callback)
        jb   11f
        cmpl $ecrit,%eax
        jb   critical_region_fixup
+       cmpl $sysexit_scrit,%eax
+       jb   11f
+       cmpl $sysexit_ecrit,%eax
+       ja   11f
+       addl $0x34,%esp                 # Remove cs...ebx from stack frame.
 11:    push %esp
        call evtchn_do_upcall
        add  $4,%esp
index 32e44f0f0152e956b308a15e3e036cf175deb109..844c87e78cd04491f648577bac509ec526d62c9b 100644 (file)
 #include <asm/pgtable.h>
 #include <asm/unistd.h>
 
+#ifdef CONFIG_XEN
+#include <xen/interface/callback.h>
+#endif
+
 extern asmlinkage void sysenter_entry(void);
 
 void enable_sep_cpu(void)
@@ -54,6 +58,18 @@ int __init sysenter_setup(void)
 {
        syscall_page = (void *)get_zeroed_page(GFP_ATOMIC);
 
+#ifdef CONFIG_XEN
+       if (boot_cpu_has(X86_FEATURE_SEP)) {
+               struct callback_register sysenter = {
+                       .type = CALLBACKTYPE_sysenter,
+                       .address = { __KERNEL_CS, (unsigned long)sysenter_entry },
+               };
+
+               if (HYPERVISOR_callback_op(CALLBACKOP_register, &sysenter) < 0)
+                       clear_bit(X86_FEATURE_SEP, boot_cpu_data.x86_capability);
+       }
+#endif
+
        if (boot_cpu_has(X86_FEATURE_SEP)) {
                memcpy(syscall_page,
                       &vsyscall_sysenter_start,